//
//  tutu_os.c
//  device
//
//  Created by ffly on 2021/3/16.
//

#include "esp_timer.h"

#include "netdb.h"

#include "xlx_define.h"
#include "xlx_core.h"

static const char* TAG = "OS";

//系统休眠时间，秒
static void tutuos_sleep(int second){
    vTaskDelay(second*1000 / portTICK_PERIOD_MS);
}

//系统休眠时间，毫秒数
static void tutuos_sleep2(int millisecond){
        vTaskDelay(millisecond / portTICK_PERIOD_MS);
}

typedef struct{
    char url[64];
    char ip[32];
}DNSCache_t;

#define DNS_CACHE_LIST_MAX 6
static DNSCache_t _sDnsCacheList[DNS_CACHE_LIST_MAX] = {0};
static char _sDnsCahceLoaded = 0;
static int _sDnsCacheListInsertIdx = 0;

//保存dns缓存到文件
static void tutuos_save_dnscache(void){
    g_tutufile.write_file(TUTU_DSN_CACHE_FILE_NAME, (char *)_sDnsCacheList, sizeof(_sDnsCacheList));
}

//加载dns缓存到内存中
static void tutuos_load_dnscache(void){
    unsigned long int len = sizeof(_sDnsCacheList);
    g_tutufile.read_file(TUTU_DSN_CACHE_FILE_NAME, (char *)_sDnsCacheList, &len);
}

//DNS解析ip
static char tutuos_getaddress(const char *url,char *ipbuf,int buflen){
    int i = 0;
    struct hostent* hostname = NULL;
    TUTU_LOGI(TAG, "%s %d(%s)	",__func__,__LINE__, url);
    hostname = gethostbyname(url);    
    //return 0;
    if(hostname != NULL  && hostname->h_addr_list[0]){        
        inet_ntop(hostname->h_addrtype,hostname->h_addr_list[0],ipbuf,buflen);
        // TUTU_LOGI(TAG, "%s %d	",__func__,__LINE__);
        for(i = 0; i < DNS_CACHE_LIST_MAX;i++){
            // TUTU_LOGI(TAG, "%s %d	",__func__,__LINE__);
            if(strlen(_sDnsCacheList[i].url) <= 1){
                //保存DNS查询成功的结果，防止下次查询失败的时候无法使用
                strncpy(_sDnsCacheList[i].url,url,64);
                strncpy(_sDnsCacheList[i].ip,ipbuf,buflen);
                tutuos_save_dnscache();
                break;
            }
        }

        //如果存满了，则循环替换
        if(i >= DNS_CACHE_LIST_MAX){
            i = _sDnsCacheListInsertIdx % DNS_CACHE_LIST_MAX;
            _sDnsCacheListInsertIdx++;

            //保存DNS查询成功的结果，防止下次查询失败的时候无法使用
            strncpy(_sDnsCacheList[i].url,url,64);
            strncpy(_sDnsCacheList[i].ip,ipbuf,buflen);
            tutuos_save_dnscache();
        }

        return 1;
    }

    if(_sDnsCahceLoaded == 0){
        _sDnsCahceLoaded = 1;
        tutuos_load_dnscache();
    }

    //查询缓存
    for(i = 0;i < DNS_CACHE_LIST_MAX;i++){
        if(0 == strncmp(url,_sDnsCacheList[i].url,64)){
            strncpy(ipbuf,_sDnsCacheList[i].ip,buflen);
            return 1;
        }
    }
    return 0;
}

static uint32_t tutuos_get_utc_timestamp(){
    return (uint32_t)(esp_timer_get_time() / 1000);
}

//把内存中的数据保存到文件
// extern void tutugps_data_savelist2file(void);
// extern void tutuat_send_cmd(char *cmdstr);

/*重启设备*/
static void tutuos_restart(){    
    // tutugps_data_savelist2file();  // 保存GPS
    TUTU_LOGI(TAG, "tutuos_restart() soft restart os!!");    
    esp_restart();
}

/**关闭设备*/
static void tutuos_power_off(){
    // tutugps_data_savelist2file();    // 保存GPS
    TUTU_LOGI(TAG, "tutuos_power_off() soft shutdown os!!");
}

static uint32_t tutuos_get_ticks(){
      return xTaskGetTickCount();
}

typedef struct rtc_time {
  int tm_sec;	//seconds [0,59]
  int tm_min;	//minutes [0,59]
  int tm_hour;  //hour [0,23]
  int tm_mday;  //day of month [1,31]
  int tm_mon;   //month of year [1,12]
  int tm_year; // since 1970
  int tm_wday; // sunday = 0
}t_rtc;
// extern void PMIC_RTC_SetTime(t_rtc *tmp,int type);

static void tutuos_set_rtc_time(TutuTime *time){
    char buf[64];
    struct tm tm = { 0 };
    tm.tm_year = time->year - 1900;
    tm.tm_mon =  time->mon-1;
    tm.tm_mday = time->day;
    tm.tm_hour = time->hour;
    tm.tm_min = time->min;
    tm.tm_sec = time->sec;
    time_t t = mktime(&tm);
    const char* time_str = asctime(&tm);
    strlcpy(buf, time_str, sizeof(buf));
    TUTU_LOGI(TAG, "Setting time: %s", time_str);
    struct timeval now = { .tv_sec = t };
    settimeofday(&now, NULL);  
}

void tutuos_get_rtc_time(TutuTime *t){
#if 1
    time_t rawtime;
    struct tm * timeinfo;

    time ( &rawtime );
    timeinfo = localtime ( &rawtime );  
    TUTU_LOGI(TAG, "The current date/time is: %s", asctime (timeinfo) );  

   t->year = timeinfo->tm_year + 1900;
   t->mon = timeinfo->tm_mon + 1;
   t->day = timeinfo->tm_mday;
   t->hour = timeinfo->tm_hour;
   t->min = timeinfo->tm_min;
   t->sec = timeinfo->tm_sec;

    TUTU_LOGI(TAG, "rtcTime:%d-%d-%d %d:%d:%d",t->year,t->mon,t->day,t->hour,t->min,t->sec);
#endif    
}

/*
* 获取本地时间
* 增加了时区
*/
void tutuos_get_local_rtc_time(TutuTime *t){
    time_t rawtime;
    struct tm * timeinfo;

    time ( &rawtime );
    timeinfo = localtime ( &rawtime );  
    TUTU_LOGI(TAG, "The current date/time is: %s", asctime (timeinfo) );  

   t->year = timeinfo->tm_year + 1900;
   t->mon = timeinfo->tm_mon + 1;
   t->day = timeinfo->tm_mday;
   t->hour = timeinfo->tm_hour;
   t->min = timeinfo->tm_min;
   t->sec = timeinfo->tm_sec;
}

// extern UINT64 Read_Crane_CPU_UID(void);
//读取flash芯片id
uint8_t QSPI_FLASH_ReadUUID(uint8_t *uuid,uint32_t *validLen){
    strcpy((char *)uuid,"12345678");
    *validLen =8 ;
    return 0;
}

//获取cpuid,输出长度为16个字符
static void tutuos_get_cpuid16(char *output){
    int i = 0;
    unsigned char c = 0;
    char *p = output;
    uint8_t uuid[32] = {0};
    
    uint32_t validLen = 0;
    validLen = 32;
    QSPI_FLASH_ReadUUID(uuid,&validLen);

    for(i = 7;i >= 0;i--){
        c = uuid[i];
        TUTUSprintf(p,"%02X",c);
        p += 2;
    }

}

static char __a_f_list[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
static void tutuos_printtest(){
    int i;
    uint8_t uuid[16] = {0};
    char uuidstr[64] = {0};
    uint8_t v = 0;
    uint32_t validLen = 16;

    QSPI_FLASH_ReadUUID(uuid,&validLen);

    for(i=0;i<validLen;i++){
        v = (uuid[i] >> 4) & 0xf;
        uuidstr[i*2] = __a_f_list[v];
        v = uuid[i] & 0xf;
        uuidstr[i*2+1] = __a_f_list[v];
    }

    TUTU_LOGI(TAG, "flash len=%lu uuid=%s\n",validLen,uuidstr);
}

static void tutuos_sync_ntp(void){
    // char at_cmd[] = "AT+ECSNTP=ntp1.aliyun.com\r\n";
    // tutuat_send_cmd(at_cmd);
}


static void tutuos_watch_dog_kick(){
    // WDT_kick();
}

// static char _pri_is_uart_log_enable = 0;
// static ARM_DRIVER_USART *_pri_UsartPrintHandle = NULL;
/**
 * 打开UART1的日志功能
 */
void OpenPrintUart()
{
#if 0    
    if (UsartPrintHandle) return;
    if (_pri_is_uart_log_enable) return;
    _pri_is_uart_log_enable = 1;
    extern ARM_DRIVER_USART Driver_USART1;
    _pri_UsartPrintHandle = &CREATE_SYMBOL(Driver_USART, 1);
    if(_pri_UsartPrintHandle == NULL)
        return;
    TUTU_LOGI(TAG, "Open enable for log");

    GPR_clockDisable(FCLK_UART1);
    GPR_setClockSrc(FCLK_UART1, FCLK_UART1_SEL_26M);
    GPR_clockEnable(FCLK_UART1);
    GPR_swReset(RST_FCLK_UART1);
    _pri_UsartPrintHandle->Initialize(NULL);
    _pri_UsartPrintHandle->PowerControl(ARM_POWER_FULL);
    _pri_UsartPrintHandle->Control(ARM_USART_MODE_ASYNCHRONOUS | ARM_USART_DATA_BITS_8 |
                        ARM_USART_PARITY_NONE       | ARM_USART_STOP_BITS_1 |
                        ARM_USART_FLOW_CONTROL_NONE, 115200ul);
    UsartPrintHandle = _pri_UsartPrintHandle;
#endif    
}
/**
 * 关闭UART1的日志功能
 */
static void ClosePrintUart()
{
#if 0    
    if (!_pri_is_uart_log_enable || !_pri_UsartPrintHandle) {
        if (UsartPrintHandle) {
            TUTU_LOGI(TAG, "Close at port disable for log");
            UsartPrintHandle = NULL;    //释放默认的打印句柄
        }
        return;
    }
    TUTU_LOGI(TAG, "Close disable for log");
    _pri_is_uart_log_enable = 0;
    _pri_UsartPrintHandle->PowerControl(ARM_POWER_OFF);
    _pri_UsartPrintHandle->Uninitialize();
    _pri_UsartPrintHandle = NULL;
    UsartPrintHandle = NULL;
    GPR_clockDisable(FCLK_UART1);
#endif    
}

static void tutuos_uart_log_enable(int enable) {
#if 0    
    if (enable && tutu_show_logger) {
        OpenPrintUart();
    } else {
        ClosePrintUart();
    }
#endif    
}

// extern size_t xPortGetTotalHeapSize(void);
// extern size_t xPortGetFreeHeapSize(void);
static unsigned long tutuos_memory_total_size() {
    //return xPortGetTotalHeapSize();
    return 0;
}

static unsigned long tutuos_memory_free_size() {    
    return esp_get_free_heap_size();;
}


static void tutu_at_cust_action_handler(char *outresult, int argc, char **argv) {
#if 0    
    if (argc > 0) {
        if (strcasecmp("atWriteImei", argv[0]) == 0 && argc > 1) {
            int ret = g_tuturil.write_imei(argv[1]);
            TUTUSnprintf(outresult, 1024, "%s %s %s", argv[0], argv[1], ret ? "ok" : "failt");
            return;
        } else if (strcasecmp("atlog", argv[0]) == 0 && argc > 1) {
            int logType = atoi(argv[1]);
            tutuos_uart_log_enable(logType);
            TUTUSnprintf(outresult, 1024, "log chan = %d", logType);
            return;
        } else if (strcasecmp("atCSQ", argv[0]) == 0) {
            tutuat_send_cmd_cb("atCSQ", "AT+CSQ\r\n", _pri_vircmd_handler);
            return;
        } else if (strcasecmp("atCGDCONT", argv[0]) == 0) {
            tutuat_send_cmd_cb("atCGDCONT", "AT+CGDCONT?\r\n", _pri_vircmd_handler);
            return;
        } else if (strcasecmp("atWifi", argv[0]) == 0) {
            argv[0] = "WifiCtrl";
        } else if (strcasecmp("atrf", argv[0]) == 0) {
            argv[0] = "CheckRf";
        }
    }    
    tutuaction_process_at_data(outresult, argc, argv);
#endif    
}

//初始化
void TutuOS_Init(TutuOS *myself){
    TUTU_LOGI(TAG, "%s(%d)", __FUNCTION__, __LINE__); 
    myself->sleep = tutuos_sleep;
    myself->sleep2 = tutuos_sleep2;
    myself->getaddress = tutuos_getaddress;
    myself->get_utc_timestamp = tutuos_get_utc_timestamp;

    myself->power_off = tutuos_power_off;
    myself->restart = tutuos_restart;

    myself->get_ticks = tutuos_get_ticks;
    myself->get_cpuid16 = tutuos_get_cpuid16;

    myself->set_rtc_time = tutuos_set_rtc_time;
    myself->get_rtc_time = tutuos_get_rtc_time;
    myself->sync_ntp = tutuos_sync_ntp;

    myself->watch_dog_kick = tutuos_watch_dog_kick;
    myself->uart_log_enable = tutuos_uart_log_enable;

    myself->get_memory_total_size = tutuos_memory_total_size;
    myself->get_memory_free_size = tutuos_memory_free_size;

    tutuos_printtest();
}
